/*
 *  Copyright 2008 The MITRE Corporation (http://www.mitre.org/). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.mitre.lattice.eav;

import javax.servlet.ServletException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.mitre.mrald.util.Config;
import org.mitre.mrald.util.MiscUtils;
import org.mitre.mrald.util.MraldError;
import org.mitre.mrald.util.MraldException;
import org.mitre.mrald.util.User;
/**
 *  This class is used to display the information contained within an
 *  EAVElement.
 *
 *@author     ghamilton
 *@created    Nov 9, 2004
 */
public class EavListTag extends BodyTagSupport
{

	String className;
	String howMany;
	String title;
	//private boolean alreadyProcessed = false;
	//This is used when there are nested selects and this element has already been processed as part of a parent
	private EavElement eavElement = null;

	boolean hasTitle = false;
	int number;
	final static String SECTION_CLOSE = "</div>\n";
	final static String SECTION_TABLE_CLOSE = "</td></tr></table></td></tr></table>\n";

	final static String SECTION_OPENER_TEMPLATE = "<div class=\"leftSideBar\"><p class=\"sideBarTitle\" ><:title:></p>";
	final static String SECTION_TABLE_OPENER_TEMPLATE = "<table summary=\"\" width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr><td class=\"bord\"><table summary=\"\" width=\"100%\" border=\"0\" cellspacing=\"1\" cellpadding=\"2\"><tr><th colspan=\"<:colspan:>\"><:title:></th></tr><tr><td colspan=\"<:colspan:>\">\n";

	final static String INSERT_ACTION = "Insert";
	final static String UPDATE_ACTION = "Update";
	final static String SELECT_ACTION = "Select";
	String action = SELECT_ACTION;

	String[] colNames = new String[1];
	String[] colValues = new String[1];

	String sortBy;

	EavListTag child = null;

	private String displayColumn = null;

	private String closingTag = "";

	private String closeTag = "";
	private String startTag = "";

	//Sometimes data is displayed as a list, sometimes as a Table
	private boolean isTable = true;

	private User user;
	/**
	 *  Constructor for the AllTablesListTag object
	 */
	public EavListTag()
	{
		if (isTable)
		{
			startTag = SECTION_TABLE_OPENER_TEMPLATE;
			closeTag = SECTION_TABLE_CLOSE;
		}
		else
		{
			startTag = SECTION_OPENER_TEMPLATE;
			closeTag = SECTION_CLOSE;
		}
		//user = ( User ) pageContext.findAttribute( Config.getProperty( "cookietag" ) );
	}



	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  title  The new title value
	 */
	public void setTitle(String title)
	{
		this.title = title;
	}


	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  title  The new title value
	 */
	protected void setUser(User user)
	{
		this.user = user;
	}

	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  title  The new title value
	 */
	protected User getUser()
	{
		return user;
	}

	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  colName  The new colName value
	 */
	public void setColName(String colName)
	{
		colNames[0] = colName;
	}


	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  colValue  The new colValue value
	 */
	public void setColValue(String colValue)
	{
		colValues[0] = colValue;
	}

	/*
	 *@param  colName  The new colName value
	 */
	public void setIsTable(boolean isTable)
	{
		this.isTable= isTable;
		if (isTable)
		{
			startTag = SECTION_TABLE_OPENER_TEMPLATE;
			closeTag = SECTION_TABLE_CLOSE;
		}
		else
		{
			startTag = SECTION_OPENER_TEMPLATE;
			closeTag = SECTION_CLOSE;
		}
	}

	public boolean getIsTable()
	{
		return this.isTable;
	}
	/**
	 *  Sets the className attribute of the FormBuilderTag object
	 *
	 *@param  className  The new className value
	 */
	public void setClassName(String className)
	{
		this.className = className;
	}


	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 *
	 *@param  howMany  The new howMany value
	 */
	public void setHowMany(String howMany)
	{
		this.howMany = howMany;
	}


	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 */
	public void setNumber()
	{
		if (howMany == null)
		{
			number = 1;
			return;
		}
		number = Integer.parseInt(howMany);
	}

	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 */
	public void setSortBy(String sortCol)
	{
		this.sortBy = sortCol;
	}

	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 */
	public String getSortBy()
	{
		return this.sortBy;
	}

	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 *
	 *@param  hasTitle  The new hasTitle value
	 */
	public void setHasTitle(boolean hasTitle)
	{
		this.hasTitle = hasTitle;
	}


	/**
	 *  This function, runs the query and returns the format on a line, by line
	 *  case
	 *
	 *@param  thisAction  The new action value
	 */
	public void setAction(String thisAction)
	{
		action = thisAction;
	}


	/**
	 *  This function, runs the query and returns the format on a line, by line
	 *  case
	 *
	 *@return    The HTML for inclusion in the second form building page.
	 */
	public String getAction()
	{
		return action;
	}


	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 *
	 *@return                   Description of the Return Value
	 *@exception  JspException  Description of the Exception
	 */
	/*
	 *  public void setParams(EavElement thisElement)
	 *  {
	 *  if (colValues[0] != null)
	 *  {
	 *  thisElement.addParams(colNames[0], colValues[0]);
	 *  }
	 *  }
	 */
	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 *
	 *@return                   Description of the Return Value
	 *@exception  JspException  Description of the Exception
	 */
	/*
	 *  public void setAlreadyProcessed(boolean processed)
	 *  {
	 *  alreadyProcessed = processed;
	 *  }
	 */
	/**
	 *  Sets the howMany attribute of the FormBuilderTag object
	 *
	 *@return                   The alreadyProcessed value
	 *@exception  JspException  Description of the Exception
	 */
	/*
	 *  public boolean getAlreadyProcessed()
	 *  {
	 *  return alreadyProcessed;
	 *  }
	 */
	/**
	 *  Description of the Method
	 *
	 *@return                   Description of the Return Value
	 *@exception  JspException  Description of the Exception
	 */
	public int doStartTag() throws JspException
	{

		try
		{

			this.user = ( User ) pageContext.getSession().getAttribute( Config.getProperty( "cookietag" ) );

			processElements();

		} catch (MraldException e)
		{
			throw new MraldError(e);
		}
		setNumber();

		return EVAL_BODY_INCLUDE;
	}


	/**
	 *  Description of the Method
	 *
	 *@param  thisTag             Description of the Parameter
	 *@exception  MraldException  Description of the Exception
	 */
/* PM: Never used!
	private void processElements(EavListTag thisTag) throws MraldException
	{
		try
		{
			this.user = ( User ) pageContext.getSession().getAttribute( Config.getProperty( "cookietag" ) );

			thisTag.setUser( this.user);
			EavElement thisElement = thisTag.castElement();

			if (thisElement == null)
			{
				throw new MraldException("EavElement is NULL");
			}
			if (thisTag.getDisplayColumn() != null)
			{
				thisElement.setDisplayColumn(thisTag.getDisplayColumn());
				//reinitialize

				thisElement.init();
			}

			thisElement.setIsTable(thisTag.getIsTable());
			thisElement.setUser(thisTag.getUser());
			//reinitialize

			//Add the where clause
			if (thisTag.colNames[0] != null)
			{
				thisElement.addParams(thisTag.colNames[0], thisTag.colValues[0]);

			}
			if (thisTag.pageContext.getRequest().getParameter("colName") != null)
			{
				String type = thisTag.pageContext.getRequest().getParameter("classType");

				if (type.equals(thisElement.getType()))
				{
					thisElement.setParams(thisTag.pageContext.getRequest().getParameter("colName"), thisTag.pageContext.getRequest().getParameter("colValue"));
					if (thisTag.pageContext.getRequest().getParameter("sortBy") != null)
						thisElement.addSort(thisTag.pageContext.getRequest().getParameter("sortBy"));

				}
			}

			if (thisTag.getSortBy() != null)
			{

					thisElement.addSort( thisTag.getSortBy());
			}
			EavListTag childTag = thisTag.getChild();
			thisTag.setEavElement(thisElement);
			childTag.setUser(this.user);

			if (childTag != null)
			{
				childTag.processElements(childTag);
			}
		} catch (ServletException e)
		{
			throw new MraldException(e);
		} catch (MraldException e)
		{
			throw new MraldException(e);
		}
	}*/


	/**
	 *  Description of the Method
	 *
	 *@exception  MraldException  Description of the Exception
	 */
	private void processElements() throws MraldException
	{
		try
		{
			this.user = ( User ) pageContext.getSession().getAttribute( Config.getProperty( "cookietag" ) );

			 //MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: doStartTag: User :" + this.user );

			eavElement = castElement();

			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: Process:   " + this.toString() +
			//	". Assigning " + eavElement.toString());

			if (eavElement == null)
			{
				throw new MraldException("EavElement is NULL");
			}
			eavElement.setIsTable(this.getIsTable());
			if (getDisplayColumn() != null)
			{
				eavElement.setDisplayColumn(displayColumn);
				//reinitialize

				eavElement.init();
			}
			//Add the where clause
			if (colNames[0] != null)
			{
				eavElement.addParams(colNames[0], colValues[0]);

			}
			eavElement.setUser(this.user);


			if (pageContext.getRequest().getParameter("colName") != null)
			{
				String type = pageContext.getRequest().getParameter("classType");

				if (type.equals(eavElement.getType()) || type.equals("ALL"))
				{

					eavElement.setParams(pageContext.getRequest().getParameter("colName"), pageContext.getRequest().getParameter("colValue"));
					if (pageContext.getRequest().getParameter("sortBy") != null)
						eavElement.addSort(pageContext.getRequest().getParameter("sortBy"));

				}
			}
			if (sortBy != null)
			{

				eavElement.addSort( sortBy);
			}

		} catch (ServletException e)
		{
			throw new MraldException(e);
		} catch (MraldException e)
		{
			throw new MraldException(e);
		}
	}


	/**
	 *  Standard entry for the Tag - kinda like main() for stand alone apps
	 *
	 *@return                   Always 0.
	 *@exception  JspException  Standard exception - ones not caught will fall
	 *      through to the ErrorPage.jsp
	 */

	public int doEndTag()
		 throws JspException
	{
		StringBuffer ret = new StringBuffer();


		try
		{
			if (action.equals(INSERT_ACTION))
			{
				//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: doEndTag:  " + this.toString() + " Action  " + action );

				if (pageContext.getRequest().getParameter("colName") != null)
				{
					eavElement.setColName(pageContext.getRequest().getParameter("colName"));
					eavElement.setColValue(pageContext.getRequest().getParameter("colValue"));
				}
				ret.append(getInsertHtml(eavElement));
			} else
			{

				EavListTag topTag = getParents(this);

				//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: doEndTag :" + this.user);

				topTag.setUser(this.user);

				if (!this.equals(topTag))
				{
					//this.close();
					this.release();
					return SKIP_BODY;
				}

				ret.append(topTag.getChildHtml(topTag));
				topTag.close();

			}
			pageContext.getOut().print(ret.toString());

		} catch (java.io.IOException e)
		{
			throw new JspException(e.getMessage());
		} catch (MraldException e)
		{
			throw new MraldError(e);
		}

		return EVAL_BODY_INCLUDE;
	}


	/**
	 *  Initializes the streams and database connections.
	 */
	public void init()
	{

	}


	/**
	 *  Gets the parents attribute of the EavListTag object
	 *
	 *@param  thisTag             Description of the Parameter
	 *@return                     The parents value
	 *@exception  MraldException  Description of the Exception
	 */
	public EavListTag getParents(EavListTag thisTag) throws MraldException
	{
		//EavListTag ancestorTag = (EavListTag) thisTag.findAncestorWithClass(thisTag, EavListTag.class);
		EavListTag ancestorTag = (EavListTag) thisTag.getParent();
		if (ancestorTag != null)
		{
			//EavElement childElement = thisTag.getEavElement();
			//EavElement parentElement = ancestorTag.getEavElement();
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: getParents: Assigning " + parentElement.toString() + " as parent to " + childElement.toString());

			ancestorTag.setChild(thisTag);
			thisTag = getParents(ancestorTag);
		}


		//return the top tag
		return thisTag;
	}


	/**
	 *  Gets the childHtml attribute of the EavListTag object
	 *
	 *@param  thisTag             Description of the Parameter
	 *@return                     The childHtml value
	 *@exception  MraldException  Description of the Exception
	 */
	public String getChildHtml(EavListTag thisTag) throws MraldException
	{
		StringBuffer ret = new StringBuffer();
		String whereCol = "";
		String whereVal = "";

		//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for:   " + this.toString());

		try
		{
			EavListTag childTag = thisTag.getChild();

			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for: 1 " + thisTag.getUser());
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for:  2 " + this.user);
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for:  3 " + childTag.getUser());

			boolean moreRows = true;
			if (childTag != null)
			{
				childTag.setUser( thisTag.getUser());

				EavElement childElement = childTag.getEavElement();

				//If there is a 'reused' version of the Tag Lib.
				//e.g.
				// ***********************
				//    <entityTag></entityTag>
				//
				//     <entityTag>
				//        <attributeTag></attributeTag>
				//    </entityTag>
				// ***********************
				//
				//then the element will have been set to null on a previous run, but the system will
				//'think' that a Child Tag still exists.
				//e.g An <ELementTag> will retain a childTag 'Attribute, although in this case it doesn't apply

				if (childElement != null)
				{
					EavElement thisElement = thisTag.getEavElement();

					//childElement.setConnection(thisElement.getConnection());

					//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavLIstTag: Child element query is  :"  + childElement.getQuery());

					//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavLIstTag: this element query is  :"  + thisElement.getQuery());
					childElement.setUser(user);

					if (thisElement != null)
					{
						thisElement.setUser(this.user);
						int i = 0;
						while (moreRows)
						{
							i++;

							String htmlStr = thisElement.selectHtmlSingle();

							//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: " +  thisTag.toString()  + " this element query is  :"  + thisTag.getEavElement().getQuery() + " html string :" + htmlStr);


							if (htmlStr == null)
							{

								moreRows = false;
								continue;
							}
							//Make sure the title is applied after the first select.
							//That way the number of columns is already known
							ret.append(thisTag.getTitle());
							ret.append(htmlStr);

							whereCol = thisElement.getColName();
							whereVal = thisElement.getColValue();

							childElement.setParams(whereCol, whereVal);

							if (childElement.getSortBy() != null)
							{
								childElement.addSort( childElement.getSortBy());
							}
							ret.append(childTag.getChildHtml(childTag));

							ret.append(thisElement.endHtmlSingle());

							ret.append(closingTag);

						}
					}
					//thisTag.setAlreadyProcessed(true);
				} else
				{
					//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for:  3 " + this.user);

					thisTag.getEavElement().setUser(this.user);

					String returnStr = getHtml(thisTag.getEavElement());

					//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: " +  thisTag.toString()  + " this element query is  :" + returnStr);
					ret.append(returnStr);

				}

			} else
			{
				//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: " +  thisTag.toString()  + " this element query is  :"  + thisTag.getEavElement().getQuery() + ". About to get html");
				//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: In getCHildHtml for:  4 " + this.user);

				thisTag.getEavElement().setUser(this.user);
				String returnStr = getHtml(thisTag.getEavElement());

				//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavLIstTag: this element query is  :"  + returnStr);
				ret.append(returnStr);
				//thisTag.setAlreadyProcessed(true);
			}
			return ret.toString();
		} catch (MraldException e)
		{
			throw e;
		}
	}


	/**
	 *  Gets the html attribute of the EavListTag object
	 *
	 *@param  thisElement         Description of the Parameter
	 *@return                     The html value
	 *@exception  MraldException  Description of the Exception
	 */
	private String getHtml(EavElement thisElement) throws MraldException
	{
		StringBuffer ret = new StringBuffer();

		for (int i = 0; i < number; i++)
		{

			String htmlStr = thisElement.selectHtml();
			ret.append(getTitle());
			//Make sure the title is applied after the first select.
			//That way the number of columns is already known
			ret.append(htmlStr);

			if (i < number - 1)
			{
				ret.append("<hr width=\"90%\">\n");
			}
		}
		if (hasTitle)
		{
			ret.append(closeTag);
		}
		return ret.toString();
	}

	/**
	 *  Gets the html attribute of the EavListTag object
	 *
	 *@param  thisElement         Description of the Parameter
	 *@return                     The html value
	 *@exception  MraldException  Description of the Exception
	 */
	private String getInsertHtml(EavElement thisElement) throws MraldException
	{
		StringBuffer ret = new StringBuffer();

		for (int i = 0; i < number; i++)
		{


			String htmlStr = thisElement.insertHtml();
			ret.append(getTitle());
			//Make sure the title is applied after the first select.
			//That way the number of columns is already known
			ret.append(htmlStr);

			if (i < number - 1)
			{
				ret.append("<hr width=\"90%\">\n");
			}
		}
		if (hasTitle)
		{
			ret.append(closeTag);
		}

		return ret.toString();
	}

	/**
	 *  Initializes the streams and database connections.
	 *
	 *@param  descendant  The new child value
	 */
	private void setChild(EavListTag descendant)
	{
		this.child = descendant;
	}


	/**
	 *  Initializes the streams and database connections.
	 *
	 *@return    The title value
	 */
	public EavListTag getChild()
	{
		return this.child;
	}


	/**
	 *@param  column  The new displayColumn value
	 */
	public void setDisplayColumn(String column)
	{
		this.displayColumn = column;
	}


	/**
	 *@return    The displayColumn value
	 */
	public String getDisplayColumn()
	{
		return this.displayColumn;
	}


	/**
	 *  Initializes the streams and database connections.
	 *
	 *@return    The title value
	 */
	public String getTitle()
	{

		try
		{

			if (hasTitle)
			{
				String outTitle = MiscUtils.replace(startTag,  "<:title:>", title);
				outTitle = MiscUtils.replace(outTitle, "<:colspan:>", eavElement.getColCount());

				//Very confusing I know. Needs to be looked at
				closingTag = closeTag;

				return outTitle;
			}
			return new String("");
		} catch (Exception e)
		{
			throw new MraldError(e);
		}
	}


	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@return    The eavElement value
	 */
	public EavElement getEavElement()
	{
		return eavElement;
	}


	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@param  thisElement  The new eavElement value
	 */
	/* PM: Never used!
	private void setEavElement(EavElement thisElement)
	{
		this.eavElement = thisElement;
	}*/


	/**
	 *  Sets the title attribute of the FormBuilderTag object
	 *
	 *@exception  MraldException  Description of the Exception
	 */
	public void close() throws MraldException
	{
		//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: Closing:   " + this.toString());

		if (eavElement != null)
		{
			eavElement.close();
			eavElement = null;
		}

		if (getChild() != null)
		{
			getChild().close();
		}
		this.release();

	}


	/**
	 *  Based on the passed in class name, uses Reflection to create the new
	 *  ParserElement objects for inclusion in the MsgObject workingObjects
	 *  HashTable.
	 *
	 *@return                       ParserElement of the class defined by the input
	 *      String
	 *@exception  ServletException  Description of the Exception
	 */
	private EavElement castElement()
		 throws ServletException
	{
		try
		{
			EavElement thisElement;

			if (className == null)
			{
				return null;
			}
			Class classDefinition = Class.forName(className);

			thisElement = (EavElement) classDefinition.newInstance();

			thisElement.setUser(this.user);
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "EavListTag: castElement " + this.user + " to Tag Object " + thisElement.toString());

			return thisElement;
		}catch (MraldException me)
		{
			ServletException ce = new ServletException("InstantiationException: of " + className + "  : " + me.getMessage());
			ce.fillInStackTrace();
			throw ce;
		}
		catch (InstantiationException wfe)
		{
			ServletException ce = new ServletException("InstantiationException: of " + className + "  : " + wfe.getMessage());
			ce.fillInStackTrace();
			throw ce;
		} catch (ClassNotFoundException cne)
		{
			ServletException ce = new ServletException("ClassNotFoundException: " + cne.getMessage());
			ce.fillInStackTrace();
			throw ce;
		} catch (IllegalAccessException iae)
		{
			ServletException ce = new ServletException("Illegal access exception: " + iae.getMessage());
			ce.fillInStackTrace();
			throw ce;
		}
	}
}

